import pickle
import sys,os
from array import array

sys.path.append(os.pardir)
import numpy as np
from dataset.mnist import load_mnist

#一般层的激活函数
def sigmoid(x):
    return 1/(1+np.exp(-x))

#输出层的softmax激活函数,将原本的处理单个元素的改为处理多各元素形式
def softmax(x):
    for i in range(len(x)):
        c = np.max(x[i])
        x[i] = np.exp(x[i] - c)/np.sum(np.exp(x[i] - c))
    return x


#获取测试数据
def get_data():
    (x_train,t_train),(x_test,t_test) = \
        load_mnist(normalize=True,flatten=True,one_hot_label=False)
    return x_test,t_test

#初始化权重（利用别人已经学习得的数据，来进行初始化）
#sample_weight.pkl文件中已字典形式保存好了我们所需要用的的权重和偏置
def init_network():
    with open("sample_weight.pkl",'rb') as f:
        network = pickle.load(f)
    return network

#使用神经网络进行分类推理
def predict(network,x):
    W1,W2,W3 = network['W1'],network['W2'],network['W3']
    b1,b2,b3 = network['b1'],network['b2'],network['b3']
    a1 = np.dot(x,W1) + b1
    z1 = sigmoid(a1)
    a2 = np.dot(z1,W2) + b2
    z2 = sigmoid(a2)
    a3 = np.dot(z2,W3) + b3
    y = softmax(a3)
    y_sum = np.sum(y)
    return y

##随机抽取指定数量的数据
def random_choice(x_train,t_train,size):
    trai_size = x_train.shape[0]  #获取训练数据的数量
    batch_size = size  #设定抽取的数量
    batch_mask = np.random.choice(trai_size,batch_size)
    x_batch = x_train[batch_mask]
    t_batch = t_train[batch_mask]
    return x_batch,t_batch

##mini_batch版本的交叉熵误差
def cross_entropy_error(y,t):
    if y.ndim == 1:  #单个数据时，处理一下
        t = t.reshape(1,t.size)
        y = y.reshape(1,y.size)
    batch_size = y.shape[0]
    return -np.sum(t * np.log(y + 1e-7))/batch_size

##mini_batch版本的交叉熵误差，当监督数据以非one-hot形式输入时
def cross_entropy_error_noonehot(y,t):
    if y.ndim == 1:  #单个数据时，处理一下
        t = t.reshape(1,t.size)
        y = y.reshape(1,y.size)
    batch_size = y.shape[0]
    return -np.sum(np.log(y[np.arange(batch_size),t] + 1e-7))/batch_size


if __name__ == '__main__':
    x,t = get_data()  #获取测试数据、监督数据
    x,t = random_choice(x,t,100)  #从中随机抽出100条，准备进行测试
    network = init_network()  #初始化权重参数
    accuracy_cnt = 0
    sum = 0
    y = predict(network,x)
    for i in range(len(y)):
        p = np.argmax(y[i])  #获取概率最高的索引（其正好就对应我们需要的答案）
        sum += np.sum(y[i])  #根据softmax函数特有的性质，这里的累加和应该为100
        if p == t[i]:
            accuracy_cnt += 1  #统计预测正确的数目
    print("Accuracy:" + str(float(accuracy_cnt) / len(x)))  # 输出识别精度，即在多大程度上能正确判断，应为：0.9352
    print("查看所有的softmax函数值:" + str(sum))  # 输出识别精度，即在多大程度上能正确判断，应为：0.9352
    loss = cross_entropy_error_noonehot(y,t)  #获取损失函数值
    print("损失函数值:"+str(loss))


